package com.qdone.framework.config;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.PreDestroy;

import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.security.MD5Encoder;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.transaction.RedissonTransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qdone.framework.util.RedisCache;

import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
 * 配置redis和redisson
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
	
	static Logger log = LoggerFactory.getLogger(RedisConfig.class);
	
	@Value("${spring.redis.host}")
	private String host;
	
	@Value("${spring.redis.port}")
	private int port;
	
	@Value("${spring.redis.password}")
	private String password;

	@Value("${spring.redis.database:0}")
	private Integer database;
	
	
	@Bean
	public KeyGenerator keyGenerator() {
		/**
		 * 自定义SpringCache缓存key
		 */
        return new KeyGenerator() {
        	@Override
        	public Object generate(Object target, Method method, Object... params) {
        		Map<String, Object> map = new HashMap<String, Object>();
        		String keyPrfex=target.getClass().getName()+"."+method.getName()+":";
        		map.put("target", target.getClass().toGenericString());//放入target的名字
        		map.put("method", method.getName());//放入method的名字
        		if (params != null && params.length > 0) {//把所有参数放进去
        			int i = 0;
        			for (Object o : params) {
        				map.put("params-" + i, o);
        				i++;
        			}
        		}
        		String str = JSONObject.toJSON(map).toString();
        		byte[] hash = null;
        		String s = null;
        		try {
        			hash = MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8"));
        			s=MD5Encoder.encode(hash).toUpperCase();//使用MD5生成位移key
        		} catch (NoSuchAlgorithmException e) {
        			e.printStackTrace();
        			log.error("init springCache keyGenerator error:",e);
        		} catch (UnsupportedEncodingException e) {
        			e.printStackTrace();
        			log.error("init springCache keyGenerator error:",e);
        		}
        		return keyPrfex+s;
        	}
        };
    }

	/**
	 * 自定义缓存策略
	 */
	@Bean
    public SimpleCacheManager simpleCacheManager(RedisTemplate<String, String> redisTemplate) {
		    SimpleCacheManager simple = new SimpleCacheManager();
		    Set<RedisCache> data=new HashSet<RedisCache>();
		    data.add(new RedisCache(redisTemplate,"view",60));//配置自定义缓存，名称和失效时间,单位秒
		    data.add(new RedisCache(redisTemplate,"defaultCache",1800));//配置自定义缓存，名称和失效时间,单位秒
		    simple.setCaches(data);
	        return simple;
	 }
	
	
	/**
	 * 定义redisTemplate
	 *  可以定义多个RedisTemplate,
	 *   本处强制全部使用集群版本RedisTemplate
	 */
	@Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        template.setEnableTransactionSupport(true);//开启事务支持
        return template;
    }
	
	/**
	 * 配置redis连接池
	 */
	@Bean
	public JedisPool jedisPool() {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxTotal(100);
		jedisPoolConfig.setTestOnBorrow(true);
		jedisPoolConfig.setMaxWaitMillis(-1);
		if (StringUtils.isNotEmpty(password)) {
			return new JedisPool(jedisPoolConfig, host, port, 0, password);
		} else {
			return new JedisPool(jedisPoolConfig, host, port, 0);
		}
	}

	/**
	 * 重写Redis序列化方式，使用Json方式:
	 * 当我们的数据存储到Redis的时候，我们的键（key）和值（value）都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer，StringRedisTemplate默认使用的是StringRedisSerializer。
	 * Spring Data JPA为我们提供了下面的Serializer：
	 * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
	 * 在此我们将自己配置RedisTemplate并定义Serializer。
	 * @param redisConnectionFactory
	 * @return
	 */
	@Bean(name="cacheClient")
	public RedisTemplate<String, Object> cacheClient(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(redisConnectionFactory);

		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		// 设置值（value）的序列化采用Jackson2JsonRedisSerializer。
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
		// 设置键（key）的序列化采用StringRedisSerializer。
		redisTemplate.setKeySerializer(new StringRedisSerializer());

		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}
	
	/**
	 * 配置redssion
	 */
	@Bean
	public RedissonClient createRedission() {
		String address="redis://"+host+":"+port;
		Config config = new Config();
		if(StringUtils.isNotEmpty(password)){
			config.useSingleServer().setConnectTimeout(40000)
			.setTimeout(30000).setDatabase(database)
			.setPassword(password)
			.setAddress(address);
		}else{
			config.useSingleServer().setConnectTimeout(40000)
			.setTimeout(30000).setDatabase(database)
			.setAddress(address);
		}
		return  Redisson.create(config);
	}
	
	 /*配置redisson支持spring事务*/
	 @Bean(name="redissonTransactionManager")
	 public RedissonTransactionManager transactionManager() {
		 RedissonTransactionManager tx= new RedissonTransactionManager(createRedission());
		 tx.setDefaultTimeout(30000);//事务超时30S
		 return tx;
	 }
	 
	 /*配置redisson支持spring自动关闭事务*/
	 @PreDestroy
	 public void destroy() {
		 createRedission().shutdown();
	 }
	 

	

}